use crate::plugin::jwt::JWTClaims;
use crate::APPLICATION_CONTEXT;
use futures::future::{ok, Either, Ready};
use ntex::http;
use ntex::service::{Middleware, Service, ServiceCall, ServiceCtx};
use ntex::web::{Error, ErrorRenderer, HttpResponse, WebRequest, WebResponse};
use rbatis::rbatis_codegen::ops::AsProxy;
use serde_json::json;

pub struct Auth;

impl<S> Middleware<S> for Auth {
    type Service = AuthMiddleware<S>;

    fn create(&self, service: S) -> Self::Service {
        AuthMiddleware { service }
    }
}

pub struct AuthMiddleware<S> {
    service: S,
}

impl<S, Err> Service<WebRequest<Err>> for AuthMiddleware<S>
where
    Err: 'static,
    S: Service<WebRequest<Err>, Response = WebResponse, Error = Error>,
    Err: ErrorRenderer,
{
    type Response = WebResponse;
    type Error = Error;
    type Future<'f> = Either<ServiceCall<'f, S, WebRequest<Err>>, Ready<Result<Self::Response, Self::Error>>> where Self: 'f, Err: 'f;

    ntex::forward_poll_ready!(service);
    ntex::forward_poll_shutdown!(service);

    fn call<'a>(&'a self, req: WebRequest<Err>, ctx: ServiceCtx<'a, Self>) -> Self::Future<'a> {
        // We only need to hook into the `start` for this middleware.
        // 获取request path
        let path = req.path();
        // 获取token
        let token = req
            .headers()
            .get("Authorization")
            .map(|v| v.to_str().unwrap_or_default().to_string())
            .unwrap_or_default()
            .split("Bearer ")
            .collect::<Vec<&str>>()
            .pop()
            .unwrap_or_default()
            .to_string();
        // 判断是否白名单
        if APPLICATION_CONTEXT.config.is_exclude(path) {
            Either::Left(ctx.call(&self.service, req))
        } else {
            let secret = &APPLICATION_CONTEXT.config.jwt_secret;
            match JWTClaims::verify(secret, &token) {
                Ok(claims) => {
                    // 获取用户id
                    let user_id = claims.id.u32();
                    // 将用户id写入req
                    req.extensions_mut().insert(user_id);
                    Either::Left(ctx.call(&self.service, req))
                }
                Err(_) => Either::Right(ok(req.into_response(
                    HttpResponse::Found()
                        .status(http::StatusCode::UNAUTHORIZED)
                        .content_type("application/json; charset=UTF-8")
                        .body(json!({
                            "code": 401,
                            "msg": "未授权访问！",
                            "data": ""
                        }))
                        .into_body(),
                ))),
            }
        }
    }
}
